package com.tuniu.agents.tools.flights.domestic.oneway;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.tuniu.agents.advisor.ChatEnvironmentMemory;
import com.tuniu.agents.message.ToolLoggerInfo;
import com.tuniu.agents.tools.flights.FlightProperties;
import com.tuniu.agents.tools.flights.model.FlightBookMpParam;
import com.tuniu.agents.tools.flights.model.FlightBookParam;
import com.tuniu.agents.tools.flights.model.Url;
import com.tuniu.agents.tools.flights.utils.CityCodeUtils;
import com.tuniu.agents.tools.flights.utils.FlightCommonUtils;
import com.tuniu.agents.util.HttpManager;
import com.tuniu.agents.util.JsonUtil;
import com.tuniu.agents.util.WebClientManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.model.ToolContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;

public class FlightInformationQueryTool implements BiFunction<FlightInformationQueryTool.Request, ToolContext, FlightInformationQueryTool.Response> {

    public static final String TOOLNAME = "FlightInformationQueryTool";
    private static final Logger logger = LoggerFactory.getLogger(FlightInformationQueryTool.class);

    private final String flightInformationQueryUrl;

    @Autowired
    private HttpManager httpManager;

    @Autowired(required = false)
    private ChatEnvironmentMemory chatEnvironmentMemory;

    public FlightInformationQueryTool(FlightProperties flightProperties) {
        this.flightInformationQueryUrl = flightProperties.getFlightInformationQueryToolUrl();
    }

    @ToolLoggerInfo
    @Override
    public Response apply(Request request, ToolContext toolContext) {
        try {
            Map<String, Object> map = toolContext.getContext();
            logger.info("FlightInformationQueryTool request parameters: {}, flightApiUrl: {}", request, flightInformationQueryUrl);
            Request requestNew = new Request(request.flightNo, (String) chatEnvironmentMemory.get((String) map.get("conversation_id")).get("queryId"));
            Response post = httpManager.post(flightInformationQueryUrl, requestNew, Response.class);
            return post.buildResponse();
        } catch (Exception e) {
            logger.error("FlightInformationQueryTool request exception: {}", e);
            return null;
        }
    }

    @JsonClassDescription("国内机票航班详情查询入参")
    public record Request(
            @JsonProperty(value = "flightNo") @JsonPropertyDescription("指定的国内航班号如MU1234，或多个国内航班号拼接MU1234-MU2233") String flightNo,
            @JsonProperty(value = "queryId") @JsonPropertyDescription("列表返回的查询快照ID") String queryId) {
    }

    @JsonClassDescription("国内机票单程航班详情查询返参")
    public record Response(
            @JsonProperty(required = true,
                    value = "successCode") @JsonPropertyDescription("响应码") boolean successCode,
            @JsonProperty(required = true,
                    value = "success") @JsonPropertyDescription("成功标识，成功：\"查询成功！\"，失败：\"查询失败！\"") String success,
            @JsonProperty(required = true,
                    value = "flightInfo") @JsonPropertyDescription("航程信息") List<FlightInfo> flightInfo,
            @JsonProperty(required = true,
                    value = "cabinInfo") @JsonPropertyDescription("舱位信息") List<CabinInfo> cabinInfo) {

        private String convertTimeFormat(String time) {
            if (StringUtils.hasText(time)) {
                return time.replace("m", "分").replace("h", "时");
            }
            return time;
        }

        public Response buildResponse() {
            List<FlightInfo> flightInfos = this.flightInfo.stream().map(flightInfo -> {
                return new FlightInfo(
                        flightInfo.departureAirport,
                        flightInfo.departureTerminal,
                        flightInfo.arrivalAirport,
                        flightInfo.arrivalTerminal,
                        flightInfo.flightNumber,
                        flightInfo.airComImageUrl,
                        flightInfo.type,
                        flightInfo.stopPoint,
                        convertTimeFormat(flightInfo.stopTime),
                        flightInfo.transferPoint,
                        convertTimeFormat(flightInfo.transferTime),
                        flightInfo.departureTime,
                        flightInfo.arrivalTime,
                        flightInfo.punctualityRate,
                        convertTimeFormat(flightInfo.totalDuration),
                        flightInfo.planModel,
                        flightInfo.shareFlightNo,
                        flightInfo.airlineCompany,
                        flightInfo.departureCityName,
                        flightInfo.arrivalCityName,
                        convertTimeFormat(flightInfo.flightTimeForTransfer),
                        flightInfo.craftType,
                        flightInfo.dTerminalForTransfer,
                        flightInfo.aTerminalForTransfer,
                        flightInfo.dTransferAirport,
                        flightInfo.aTransferAirport,
                        flightInfo.transferArrivalTime,
                        flightInfo.transferDepartureTime
                 );
            }).toList();

            return new Response(
                    this.successCode,
                    this.success,
                    flightInfos,
                    this.cabinInfo
            );
        }
    }

    @JsonClassDescription("航程信息")
    public record FlightInfo(
            @JsonProperty(value = "departureAirport") @JsonPropertyDescription("出发机场") String departureAirport,
            @JsonProperty(value = "departureTerminal") @JsonPropertyDescription("出发航站楼") String departureTerminal,
            @JsonProperty(value = "arrivalAirport") @JsonPropertyDescription("到达机场") String arrivalAirport,
            @JsonProperty(value = "arrivalTerminal") @JsonPropertyDescription("到达航站楼") String arrivalTerminal,
            @JsonProperty(value = "flightNumber") @JsonPropertyDescription("航班号") String flightNumber,
            @JsonProperty(value = "airComImageUrl") @JsonPropertyDescription("航司图标url") List<String> airComImageUrl,
            @JsonProperty(value = "type") @JsonPropertyDescription("航班类型：直飞，中转，联程，经停") String type,
            @JsonProperty(value = "stopPoint") @JsonPropertyDescription("经停城市") String stopPoint,
            @JsonProperty(value = "stopTime") @JsonPropertyDescription("经停时长") String stopTime,
            @JsonProperty(value = "transferPoint") @JsonPropertyDescription("中转城市") String transferPoint,
            @JsonProperty(value = "transferTime") @JsonPropertyDescription("中转时长") String transferTime,
            @JsonProperty(value = "departureTime") @JsonPropertyDescription("出发时间") String departureTime,
            @JsonProperty(value = "arrivalTime") @JsonPropertyDescription("到达时间") String arrivalTime,
            @JsonProperty(value = "punctualityRate") @JsonPropertyDescription("准点率") String punctualityRate,
            @JsonProperty(value = "totalDuration") @JsonPropertyDescription("总时长") String totalDuration,
            @JsonProperty(value = "planModel") @JsonPropertyDescription("机型大小，1 大，2 中，3 小") String planModel,
            @JsonProperty(value = "shareFlightNo") @JsonPropertyDescription("实际承运航班号") String shareFlightNo,
            @JsonProperty(value = "airlineCompany") @JsonPropertyDescription("航司") String airlineCompany,
            @JsonProperty(value = "departureCityName") @JsonPropertyDescription("出发城市") String departureCityName,
            @JsonProperty(value = "arrivalCityName") @JsonPropertyDescription("到达城市") String arrivalCityName,
            @JsonProperty(value = "flightTimeForTransfer") @JsonPropertyDescription("航班飞行时长") String flightTimeForTransfer,
            @JsonProperty(value = "craftType") @JsonPropertyDescription("机型") String craftType,
            @JsonProperty(value = "dTerminalForTransfer") @JsonPropertyDescription("中转航班出发航站楼") String dTerminalForTransfer,
            @JsonProperty(value = "aTerminalForTransfer") @JsonPropertyDescription("中转航班到达航站楼") String aTerminalForTransfer,
            @JsonProperty(value = "dTransferAirport") @JsonPropertyDescription("中转航班出发机场") String dTransferAirport,
            @JsonProperty(value = "aTransferAirport") @JsonPropertyDescription("中转航班到达机场") String aTransferAirport,
            @JsonProperty(value = "transferArrivalTime") @JsonPropertyDescription("中转航班到达时间") String transferArrivalTime,
            @JsonProperty(value = "transferDepartureTime") @JsonPropertyDescription("中转航班出发时间") String transferDepartureTime) {

        public FlightInfo buildFlightInfo(String departureCityName, String arrivalCityName) {
            return new FlightInfo(
                    this.departureAirport, this.departureTerminal, this.arrivalAirport, this.arrivalTerminal, this.flightNumber,
                    this.airComImageUrl, this.type, this.stopPoint, this.stopTime, this.transferPoint, this.transferTime,
                    this.departureTime, this.arrivalTime, this.punctualityRate, this.totalDuration, this.planModel, this.shareFlightNo,
                    this.airlineCompany, departureCityName, arrivalCityName, this.flightTimeForTransfer, this.craftType,
                    this.dTerminalForTransfer, this.aTerminalForTransfer, this.dTransferAirport, this.aTransferAirport,
                    this.transferArrivalTime, this.transferDepartureTime
            );
        }
    }

    @JsonClassDescription("舱位信息")
    public record CabinInfo(
            @JsonProperty(value = "priceWithTax") @JsonPropertyDescription("成人含税价格") String priceWithTax,
            @JsonProperty(value = "childPriceWithTax") @JsonPropertyDescription("儿童含税价格") String childPriceWithTax,
            @JsonProperty(value = "infPriceWithTax") @JsonPropertyDescription("婴儿含税价格") String infPriceWithTax,
            @JsonProperty(value = "price") @JsonPropertyDescription("成人票面价格") String price,
            @JsonProperty(value = "childPrice") @JsonPropertyDescription("儿童票面价格") String childPrice,
            @JsonProperty(value = "infPrice") @JsonPropertyDescription("婴儿票面价格") String infPrice,
            @JsonProperty(value = "buyCondition") @JsonPropertyDescription("购买条件") String buyCondition,
            @JsonProperty(value = "saleControl") @JsonPropertyDescription("销售控制") List<String> saleControl,
            @JsonProperty(value = "refundChangeRule") @JsonPropertyDescription("退改签规则") String refundChangeRule,
            @JsonProperty(value = "baggageInfo") @JsonPropertyDescription("行李信息") String baggageInfo,
            @JsonProperty(value = "reimbursementProof") @JsonPropertyDescription("报销凭证") String reimbursementProof,
            @JsonProperty(value = "remainingSeats") @JsonPropertyDescription("剩余座位数") String remainingSeats,
            @JsonProperty(value = "sourceId") @JsonPropertyDescription("资源ID") String sourceId,
            @JsonProperty(value = "mealCode") @JsonPropertyDescription("餐食") String mealCode,
            @JsonProperty(value = "cabinClass") @JsonPropertyDescription("舱等") String cabinClass,
            @JsonProperty(value = "url") @JsonPropertyDescription("收藏链接") Url url) {

        public CabinInfo buildCabinInfo(Url url){
            return new CabinInfo(
                    this.priceWithTax,
                    this.childPriceWithTax,
                    this.infPriceWithTax,
                    this.price,
                    this.childPrice,
                    this.infPrice,
                    this.buyCondition,
                    this.saleControl,
                    this.refundChangeRule,
                    this.baggageInfo,
                    this.reimbursementProof,
                    this.remainingSeats,
                    this.sourceId,
                    this.mealCode,
                    this.cabinClass,
                    url
            );
        }
    }

    public static Response buildCard(String flightsJson, Map<String,Object> hashMap) {
        Response response = JsonUtil.fromJson(flightsJson, FlightInformationQueryTool.Response.class);
        if(!response.successCode){
            return null;
        }
        buildDetailUrl(response, hashMap);
        return response;
    }

    public static void buildDetailUrl(Response result, Map<String, Object> qParams) {

        try {
            List<FlightInfo> flightInfos = result.flightInfo;
            List<CabinInfo> cabinInfos = result.cabinInfo;

            if (CollectionUtils.isEmpty(flightInfos) || CollectionUtils.isEmpty(cabinInfos)) {
                logger.info("FlightInformationQueryTool buildDetailUrl() flightInfos or cabinInfos is empty");
                return;
            }
            for(int i=0; i<flightInfos.size(); i++){
                FlightInfo originFlightInfo = flightInfos.get(i);
                FlightInfo updateFlightInfo = originFlightInfo.buildFlightInfo(qParams.get("departureCityName").toString(), qParams.get("arrivalCityName").toString());
                flightInfos.set(i,updateFlightInfo);
            }

            HttpManager httpManager = new WebClientManager();
            //CityCodeUtils cityCodeUtils = new CityCodeUtils();
            CityCodeUtils.Ext departureCityExt = CityCodeUtils.getCityCode(httpManager, (String) qParams.get("departureCityName"));
            CityCodeUtils.Ext arrivalCityExt = CityCodeUtils.getCityCode(httpManager, (String) qParams.get("arrivalCityName"));

            FlightInfo flightInfo = flightInfos.get(0);
            for (int i = 0; i < cabinInfos.size(); i++) {
                CabinInfo cabinInfo = cabinInfos.get(i);
                if (cabinInfos.get(i) == null) {
                    logger.info("FlightInformationQueryTool buildDetailUrl() Cabin details are empty");
                    continue;
                }

                // Get cabin code and supplier ID
                Map<String, String> map;
                if (flightInfo.flightNumber().contains("-")) {
                    map = FlightCommonUtils.analysisPriceInfoIdForTrans(cabinInfo.sourceId);
                } else {
                    map = FlightCommonUtils.analysisPriceInfoId(cabinInfo.sourceId);
                }

                Url url = new Url();
                url.setAppUrl(buildAppUrl(flightInfo, cabinInfo, qParams, map));
                url.setMpUrl(buildMpUrl(flightInfo, cabinInfo, qParams, map, departureCityExt, arrivalCityExt));
                CabinInfo updateCabinInfo = cabinInfo.buildCabinInfo(url);
                cabinInfos.set(i,updateCabinInfo);
            }

        } catch (Exception e) {
            logger.info("FlightInformationQueryTool buildDetailUrl() exception: {}", e.getMessage());
        }
    }

    private static String buildAppUrl(FlightInfo flightInfo, CabinInfo cabinInfo, Map<String, Object> qParams, Map<String, String> paramMap) {
        try {
            FlightBookParam bookParam = new FlightBookParam();
            bookParam.setQueryId((String)qParams.get("queryId"));
            bookParam.setVendorId(paramMap.get("vendorId"));
            bookParam.setCabinCodes(paramMap.get("cabinCode"));
            bookParam.setFlightNos(flightInfo.flightNumber);
            bookParam.setPriceInfoId(cabinInfo.sourceId);
            bookParam.setOrgCityName((String)qParams.get("departureCityName"));
            bookParam.setDstCityName((String)qParams.get("arrivalCityName"));
            bookParam.setDepartureDate((String)qParams.get("departureDate"));
            bookParam.setSystemId(FlightCommonUtils.DOMESTIC_FLIGHT_SYSTEM_ID);
            String param = JsonUtil.toJson(bookParam);
            String encodedJson = URLEncoder.encode(param, StandardCharsets.UTF_8);
            return "tuniuapp://page?iosPageName=TNAirplaneTicketOrderViewController"
                    + "&androidPageName=com.tuniu.plane.activity.PlaneDomesticFillOrderActivity"
                    + "&pluginType=3&parameters=" + encodedJson;
        } catch (Exception e) {
            logger.info("FlightInformationQueryTool buildAppUrl() exception: {}", e.getMessage());
            return null;
        }
    }

    private static String buildMpUrl(FlightInfo flightInfo, CabinInfo cabinInfo, Map<String, Object> qParams, Map<String, String> paramMap, CityCodeUtils.Ext departureCityExt, CityCodeUtils.Ext arrivalCityExt){
        try{
            FlightBookMpParam mpParam = new FlightBookMpParam();
            mpParam.setCabinCodes(paramMap.get("cabinCode"));
            mpParam.setFlightNos(flightInfo.flightNumber);
            mpParam.setPriceInfoId(cabinInfo.sourceId);
            mpParam.setQueryId((String) qParams.get("queryId"));
            mpParam.setVendorId(paramMap.get("vendorId"));
            mpParam.setSessionId((String) qParams.get("conversation_id"));
            mpParam.setDepartCityName((String) qParams.get("departureCityName"));
            mpParam.setArriveCityName((String) qParams.get("arrivalCityName"));
            mpParam.setChildQuantity("0");
            mpParam.setBabyQuantity("0");
            mpParam.setdCityIataCode(departureCityExt.getIata_code());
            mpParam.setaCityIataCode(arrivalCityExt.getIata_code());
            List<String> cabinTypes = convertCabinClass(cabinInfo.cabinClass);
            mpParam.setCabinType(String.join("-", cabinTypes));
            mpParam.setStartDate((String)qParams.get("departureDate"));
            mpParam.setInitCityCode(departureCityExt.getCode());
            mpParam.setDestCityCode(arrivalCityExt.getCode());

            return "/flight/pages/onlinebook/index?params=" + URLEncoder.encode(JsonUtil.toJson(mpParam), StandardCharsets.UTF_8);
        } catch (Exception e) {
            logger.info("FlightInformationQueryTool buildMpUrl() exception: {}", e.getMessage());
            return null;
        }
    }

    private static List<String> convertCabinClass(String cabinClasses) {
        List<String> cabinTypeList = new ArrayList<>();
        for (String cabinClass : cabinClasses.split("-")) {
            switch (cabinClass) {
                case "经济舱":
                    cabinTypeList.add("1");
                    break;
                case "公务舱":
                    cabinTypeList.add("2");
                    break;
                case "头等舱":
                    cabinTypeList.add("3");
                    break;
                case "超级经济舱":
                    cabinTypeList.add("4");
                    break;
            }
        }
        return cabinTypeList;
    }

    public static String buildMarkdown(Response response){
        StringBuilder markdown = new StringBuilder();
        markdown.append("| 航班号 | 出发时间 | 到达时间 | 出发机场 | 到达机场 | 机型 | 航空公司 | 出发城市 | 到达城市 |\n");
        markdown.append("| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n");

        for (FlightInfo flightInfo : response.flightInfo()) {
            markdown.append("| ").append(flightInfo.flightNumber())
                    .append("| ").append(flightInfo.departureTime())
                    .append("| ").append(flightInfo.arrivalTime())
                    .append("| ").append(flightInfo.departureAirport())
                    .append("机场| ").append(flightInfo.arrivalAirport())
                    .append("机场| ").append(flightInfo.craftType())
                    .append("| ").append(flightInfo.airlineCompany())
                    .append("| ").append(flightInfo.departureCityName())
                    .append("| ").append(flightInfo.arrivalCityName())
                    .append("|\n");
        }
        markdown.append("\n");

        markdown.append("| 成人价格 | 退改签规则 | 行李额信息 | 余位 | 舱等 |\n");
        markdown.append("| --- | --- | --- | --- | --- |\n");

        for (CabinInfo cabinInfo : response.cabinInfo) {
            markdown.append("| ").append(cabinInfo.priceWithTax())
                    .append("| ").append(cabinInfo.refundChangeRule())
                    .append("| ").append(cabinInfo.baggageInfo())
                    .append("| ").append(cabinInfo.remainingSeats())
                    .append("| ").append(cabinInfo.cabinClass())
                    .append("|\n");
        }
        markdown.append("\n");

        return markdown.toString();
    }
}

